  // CheckersBoard Engine API - Native Code Layer
 // Refactored from Martin Fierz's Checkers Board Front-End Application
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "API.h"


void InitCB_API(CB_APIStruct *s) 
{
	memset(s, 0, sizeof(CB_APIStruct));
	InitializeCriticalSection(&s->engine_criticalsection);
}

CB_APIStruct* InitializeCBEngine(wchar_t* engineFilePath, wchar_t* szError)
{
	// init engine structure
	CB_APIStruct* s = new CB_APIStruct();
	InitCB_API(s);

	// load the library
	s->hModule = ::LoadLibrary(engineFilePath);
	if(s->hModule == INVALID_HANDLE_VALUE) 
	{
		wsprintf(szError, L"LoadLibrary failed.");
		delete s;
		return NULL;	
	}

	// map functions
	s->enginecommand = (COMMANDPROC) GetProcAddress(s->hModule,"enginecommand");
	s->enginename =  (PROC3) GetProcAddress(s->hModule, "enginename");
	s->getmove =  (PROC1)GetProcAddress(s->hModule, "getmove");
	s->islegal= (PROC6)GetProcAddress(s->hModule,"islegal");			

	if(s->enginecommand == NULL || s->enginename == NULL || s->getmove == NULL) 
	{
		wsprintf(szError, L"Mapping functions failed.");
		delete s;
		return NULL;	
	}

	// check the book
	char reply[ENGINECOMMAND_REPLY_SIZE];
	if(enginecommand(s, "get book", reply))
	{
		s->togglebook = atoi(reply);
	}

	return s;
}

void DestroyCBEngine(CB_APIStruct* s)
{
	// free the library
	if(s->hModule != NULL) {
		::FreeLibrary(s->hModule);
	}
	// free CB engine structure
	delete s;
}


 // Engine methods
//////////////////////////////////////////////////////

int set_more_engine_options(CB_APIStruct *s, MORE_ENGINE_OPTIONS *options)
{
	char command[MAX_PATH + 50], reply[ENGINECOMMAND_REPLY_SIZE];

	/* See if max_dbpieces changed. */
	if (options->flags & FLAG_MAX_DB_PIECES) {
		sprintf(command, "set max_dbpieces %d", options->max_dbpieces);
		enginecommand(s, command, reply);
	}

	/* See if the wld_directory changed. */
	if (options->flags & FLAG_WDL_DIR) {
		sprintf(command, "set dbpath %s", options->wld_directory);
		enginecommand(s, command, reply);
	}

	/* See if the mtc_directory changed. */
	if (options->flags & FLAG_MTC_DIR) {
		sprintf(command, "set mtcpath %s", options->mtc_directory);
		enginecommand(s, command, reply);
	}

	/* See if the bookfile changed. */
	if (options->flags & FLAG_BOOK_FILE) {
		sprintf(command, "set bookfile %s", options->book_filename);
		enginecommand(s, command, reply);
	}

	/* See if enable_wld changed. */
	if (options->flags & FLAG_ENABLE_WDL) {
		sprintf(command, "set enable_wld %d", options->wld_egdb_enable);
		enginecommand(s, command, reply);
	}

	/* See if enable_mtc changed. */
	if (options->flags & FLAG_ENABLE_MTC) {
		sprintf(command, "set enable_mtc %d", options->mtc_egdb_enable);
		enginecommand(s, command, reply);
	}

	/* See if search_threads changed. */
	if (options->flags & FLAG_SEARCH_THREADS) {
		sprintf(command, "set searchthreads %d", options->search_threads);
		enginecommand(s, command, reply);
	}
	return(1);
}

int get_more_engine_options(CB_APIStruct *s, MORE_ENGINE_OPTIONS *options)
{
	char reply[MAX_PATH];
	memset(options, 0, sizeof(MORE_ENGINE_OPTIONS));

	/* Check if the engine supports the "dbpath" engine command. */
	if (enginecommand(s, "get dbpath", options->wld_directory)) {
		options->flags |= FLAG_WDL_DIR;
		
	}

	/* Check if the engine supports the "enable_wld" engine command. */
	if (enginecommand(s, "get enable_wld", reply)) {
		options->wld_egdb_enable = atoi(reply);
		options->flags |= FLAG_ENABLE_WDL;
	}

	/* Check if the engine supports the "max_dbpieces" engine command. */
	if (enginecommand(s, "get max_dbpieces", reply)) {
		options->max_dbpieces = atoi(reply);
		options->flags |= FLAG_MAX_DB_PIECES;
	}

	/* Check if the engins supports the "enable_mtc" engine command. */
	if (enginecommand(s, "get enable_mtc", reply)) {
		options->flags |= FLAG_ENABLE_MTC;
		options->mtc_egdb_enable = atoi(reply);
	}

	/* Check if the engine supports the "mtcpath" engine command. */
	if (enginecommand(s, "get mtcpath", options->mtc_directory)) {
		options->flags |= FLAG_MTC_DIR;
	}

	/* Check if the engine supports the "bookfile" engine command. */
	if (enginecommand(s, "get bookfile", options->book_filename)) {
		options->flags |= FLAG_BOOK_FILE;
	}

	/* Check if the engine supports the "searchthreads" engine command. */
	if (enginecommand(s, "get searchthreads", reply)) {
		options->search_threads = atoi(reply);
		options->flags |= FLAG_SEARCH_THREADS;
	}

	return(1);
}

int setengineoptions(CB_APIStruct *s, ENGINE_OPTIONS *options)
{
	char commandstring[256];
	char reply[256];
	
	if(options->flags & FLAG_HASH_SIZE)
	{
		sprintf(commandstring,"set hashsize %i",options->hash_MB);
		enginecommand(s, commandstring,reply);
	}

	if(options->flags & FLAG_EGTB_SIZE)
	{
		sprintf(commandstring,"set dbmbytes %i",options->db_MB);
		enginecommand(s, commandstring,reply);
	}
			
	if(options->flags & FLAG_BOOK)
	{
		sprintf(commandstring,"set book %i",options->book);
		enginecommand(s, commandstring,reply);
	}
	
	// set all scores on/off
	if(options->flags & FLAG_ALL_SCORES)
	{
		sprintf(commandstring,"set allscores %i",options->allscores);
		enginecommand(s, commandstring,reply);
	}
	
	return 1;
}	


//get current options for current engine
int getengineoptions(CB_APIStruct* s, ENGINE_OPTIONS *options)
{
	char reply[256];
	memset(options, 0, sizeof(ENGINE_OPTIONS));

	// hashtable size
	if(enginecommand(s, "get hashsize",reply))
	{
		options->hash_MB = atoi(reply);
		options->flags |= FLAG_HASH_SIZE;
	}

	// egtb cache size
	if(enginecommand(s, "get dbmbytes",reply))
	{
		options->db_MB = atoi(reply);
		options->flags |= FLAG_EGTB_SIZE;
	}	

	// opening book
	if(enginecommand(s, "get book",reply)) {
		options->book = atoi(reply);
		options->flags |= FLAG_BOOK;
	}
		
	// allscores
	if(enginecommand(s, "get allscores",reply))
	{
		options->allscores = atoi(reply);
		options->flags |= FLAG_ALL_SCORES;
	}
		
	return 1;
}



int getMove(CB_APIStruct* s, int* board, int color, double maxtime, CBmove* move)
{
	// check the DLL function
	if(s->getmove == NULL)
		return -1;

	setenginebusy(s, TRUE);

	// init variables
	strcpy_s(s->engineOutput, "");
	s->playNow = 0;
	s->abortEngine = 0;

	// TO-DO: find out "info" and "unused" parameter
	int result = s->getmove(board, color, maxtime, s->engineOutput, &s->playNow, 0, 0, move);
	
	setenginebusy(s, FALSE);
	return result;
}


int isLegal(CB_APIStruct *s,  int color, int* board, int from, int to, CBmove* move)
{
	// if checking moves not supported, exit with error code
	if(s->islegal == NULL)
		return -1;

	// check the move
	if(s->islegal(board, color, from, to, move) != 0) 
	{
		return 1;
	}

	return  0;
}


int enginename(CB_APIStruct* s, char Lstr[256])
{
	// set a default
	sprintf(Lstr,"no engine found");

	if(enginecommand(s, "name",Lstr))
	{
		return 1;
	}

	return 0;
}


// returns the game type
int gametype(CB_APIStruct* s)
{
	// returns the game type which the current engine plays
	// if the engine has no game type associated, it will return 21 for english checkers
	char reply[ENGINECOMMAND_REPLY_SIZE];
	char command[256];

	sprintf(reply,"");
	sprintf(command,"get gametype");

	if(enginecommand(s, command,reply))
		return atoi(reply);

	// return default game type
	return 21;
}

int enginecommand(CB_APIStruct* s, char command[256], char reply[ENGINECOMMAND_REPLY_SIZE])
{
	int result=0;
	sprintf(reply,"");

	result = s->enginecommand(command,reply);	
		
	return result;
}

// aborts the engine - that is without passing the calculated move
void abortengine(CB_APIStruct *s)
{
	clock_t t0;

	if (!getenginebusy(s))
		return;

	// set CB engine termination flags
	s->playNow = 1;
	s->abortEngine = 1;

	t0 = clock();
	while (getenginebusy(s)) {
		Sleep(10);
		if ((int)(clock() - t0) > 1000)
			break;
	}
}


 // Synchronization functions
//////////////////////////////////////////////////////

int setenginestarting(CB_APIStruct* s, int value)
{
	s->enginestarting = (bool)value;
	return 1;
}

int setenginebusy(CB_APIStruct* s, int value)
{
	EnterCriticalSection(&s->engine_criticalsection);
	s->enginebusy = (bool)value;
	LeaveCriticalSection(&s->engine_criticalsection);
	return 1;
}

int getenginestarting(CB_APIStruct* s)
{
	int returnvalue;
	returnvalue = s->enginestarting;
	return returnvalue;
}

int getenginebusy(CB_APIStruct* s)
{
	int returnvalue;
	EnterCriticalSection(&s->engine_criticalsection);
	returnvalue = s->enginebusy;
	LeaveCriticalSection(&s->engine_criticalsection);
	return returnvalue;
}
